using System;
using System.Data;
using System.Collections.Generic;
using System.Data.SQLite;

namespace DatastoreLib {
	
	public class ElevatorLogging {
		
		//# Private props
		private string connectionString;
		private IDbConnection dbcon;
		
		//# Simple Constructor
		public ElevatorLogging() : this("Twittevator") {}
		
		//# Real Constructor
		public ElevatorLogging(string databaseName) {
			//## init connectionString
            connectionString = "URI=file:sqlite_" + databaseName + ".db3";
			
			//# setup database
			initializeDatabase();
		}
		
		private void connOpen() {
			dbcon = (IDbConnection) new SQLiteConnection(connectionString);
			dbcon.Open();
		}
		
		private void connClose() {
			dbcon.Close();
			dbcon = null;
		}
		
		//# Private method to initialize the database (create table if required)
		private void initializeDatabase() {
            try
            {
                // open connection
                connOpen();
            }
            catch (SQLiteException ex)
            {
                throw new SQLiteException(ex.Message);
            }

			// execute sql command
			IDbCommand dbcmdCreateTable = dbcon.CreateCommand();
			string sqlCreateTable =
				"CREATE TABLE logs ( " +
				"	type varchar(20), " +
				"	message blob, " +
				"	timestamp varchar(50) " +
				");";
			dbcmdCreateTable.CommandText = sqlCreateTable;
			try {
				dbcmdCreateTable.ExecuteNonQuery();
				Console.WriteLine("ElevatorLogging: table created");
			} catch(Exception e) {
				if(e.Message.Contains("table logs already exists")) {
					IDbCommand dbcmdGetTableCount = dbcon.CreateCommand();
					string sqlGetTableCount = "SELECT COUNT(*) FROM logs";
					dbcmdGetTableCount.CommandText = sqlGetTableCount;
					try {
						string count = dbcmdGetTableCount.ExecuteScalar().ToString();
						Console.WriteLine("ElevatorLogging: Table already exists and has "+count+" entries.");
					} catch(Exception e2) {
						Console.WriteLine("ElevatorLogging: Table already exists, but getting entry count failed. Error: "+e2.Message);
					}
					dbcmdGetTableCount.Dispose();
					dbcmdGetTableCount = null;
				} else {
					Console.WriteLine("ElevatorLogging: Could not create table. Error: " + e.Message);
				}
			}
			
			// clean up (close command and connection)
			dbcmdCreateTable.Dispose();
			dbcmdCreateTable = null;
			connClose();
		}
		
		//# Add a entry to the database
		public bool addEntry(string type, string message) {
			
			// check if type is acceptable
			if(type.Length>20) {
				Console.WriteLine("ElevatorLogging: ERROR: Cannot add entry to database table. Data for column 'type' is too long (max 20 chars). Given type: "+type);
				return false;
			}
			
			// check if message is acceptable. message CAN be longer (SQLite limit = 1,000,000,000). However, 1000 chars should be enough.
			if(message.Length > 1000) {
				Console.WriteLine("");
				return false;
			}
			
			// if type and message are both empty, tell the method calling process to fuck off and play some rusian roulette
			if(type.Length==0 && message.Length==0) {
				Console.WriteLine("ElevatorLogging: ERROR: Hello good sir. Sadly, you did not fill in any values for type and message. Therefore, we are going to play russian roulette.");
				Console.WriteLine(" --------- Russian roulette ---------");
				Random random = new Random();
				int bulletChamber = random.Next(5);
				Console.WriteLine("Bullet is in random chamber");
				for(int chamber = 0; chamber<6; chamber++) {
					if(bulletChamber==chamber) {
						Console.WriteLine("Boom! the gun just fired!");
						if(chamber%2 == 0) {
							Console.WriteLine("Too bad, you lost russian roulette.");
							Environment.Exit(-1);
						} else {
							Console.WriteLine("You're lucky, I'll be nice and just return this method with false.");
							return false;
						}
					} else {
						Console.WriteLine("*Click* "+((chamber%2 == 0)?"you":"me")+" haz luck.");
					}
				}
				Console.WriteLine("--------------------------- ");
			}
			
			// give a warning if the user tries to submit a logging entry with no type filled in.
			if(type.Length==0) {
				Console.WriteLine("ElevatorLogging: WARNING: Given value for 'type' is empty. Please give a value for type so it can be searched for lateron");
			}
			
			// give a warning if the user tries to submit a logging entry with no message filled in.
			if(message.Length==0) {
				Console.WriteLine("ElevatorLogging: WARNING: given value for 'message' is empty. Please give a value for message so the entry becomes actually usefull");
			}
			
			// open connection
			connOpen();
			
			// execute sql insert
			IDbCommand dbcmd = dbcon.CreateCommand();
			string sql = "" +
				"INSERT INTO logs (type, message, timestamp) " +
				"VALUES ( '"+type+"', '"+message+"', '"+DateTime.Now.ToString()+"')";
			dbcmd.CommandText = sql;
			try {
				dbcmd.ExecuteNonQuery();
			} catch(Exception e) {
				// something went wrong, lets print error to console.
				Console.WriteLine("Can I haz addEntry() error?: " + e.Message + e.ToString());
			}
			// clean up: close command
			dbcmd.Dispose();
			dbcmd = null;
			
			// clean up: close connection
			connClose();
			
			// finished :)
			return true;
		}
		
		//# Get a complete list of entries filtered by type and ammount (0 is everything, else ammount is x last entry's)
		public List<LoggingEntry> retrieveEntries(string filterType, int filterAmmount) {
			// sql query filterType clause (where)
			string sqlWhere = "";
			if(filterType.Length>0) {
				sqlWhere = "WHERE type = '"+filterType+"' ";
			}
			
			// sql query filterAmmount clause (limit)
			string sqlLimit = "";
			if(filterAmmount>0) {
				sqlLimit = "LIMIT 0, "+filterAmmount+" ";
			}
			
			// create emtpy List<LoggingEntry>
			List<LoggingEntry> entryList = new List<LoggingEntry>();
			
			// open connection
			connOpen();
			
			// execute sql query
			IDbCommand dbcmd = dbcon.CreateCommand();
			string sql =
				"SELECT ROWID, type, message, timestamp " +
				"FROM logs " +
				sqlWhere +
				"ORDER BY ROWID DESC " +
				sqlLimit +
				"; ";
			dbcmd.CommandText = sql;
			try {
				IDataReader reader = dbcmd.ExecuteReader();
				
				// loop trough data
				while(reader.Read()) {
					long rowID = reader.GetInt64(0);
					string type = reader.GetString(1);
					string message = reader.GetString(2);
					string timestamp = reader.GetString(3);
					entryList.Add(new LoggingEntry(rowID, type, message, timestamp));
				}
				
				// clean up: close reader
				reader.Close();
				reader = null;
			} catch(Exception e) {
				// something went wrong, lets print error to console and add one entry to entryList with error mesg.
				Console.WriteLine("Can I haz retrieveEntries() error?: " + e.Message);
				entryList.Add(new LoggingEntry(99999, "ERROR", "Error: couldn't retrieve entries from database. Check console/log for details.", ""));
			}
			// clean up: close command
			dbcmd.Dispose();
			dbcmd = null;
			
			// clean up: close connection
			connClose();
			
			// return list
			return entryList;
		}
		
		public List<LoggingEntry> retrieveEntries(string type) {
			return retrieveEntries(type, 0);
		}
		
		public List<LoggingEntry> retrieveEntries(int ammount) {
			return retrieveEntries("", ammount);
		}
		
		public List<LoggingEntry> retrieveEntries() {
			return retrieveEntries("", 0);
		}
		
		
		//# static stuff belongs below this line ----------------------------
		/* //DEPRICATED	this functionality is now located in Storage.cs
		private static ElevatorLogging self = new ElevatorLogging();
		
		public static bool arivedAt(string level) {
			return self.addEntry("arived at", level);
		}
		
		public static bool arivedAt(int level) {
			return arivedAt(level.ToString());
		}
		
		public static bool emergencyBreak(string message) {
			return self.addEntry("emergency break", message);
		}
		
		public static bool emergencyBreak() {
			return emergencyBreak("");
		}
		
		public static bool movingTo(string level) {
			return self.addEntry("moving to", level);
		}
		
		public static bool movingTo(int level) {
			return movingTo(level.ToString());
		}
		*/
	}
}

